1. 静态链接重定位

  • 什么是链接重定位?

    • 链接时候进行符号表地址重定位,因为编译器无法去全局照顾每一个符号,尤其是模块间的函数调用

    • 举例:函数调用

image

  • 对编译期间的 main.o进行反汇编(objdump),查看汇编代码可以发现:main里针对函数my_test的符号解析 填充的其实是一个错误值,是不对的。如下

image

  • 链接完成之后呢?再次针对main进行反汇编(objdump),就会发现,链接时候已经做了填充,如下:

image

2. 动态连接技术

  • 举个例子先

    • main.c 需要链接lib1.so执行。首先编译(.o),链接(ELF),反汇编(objdump),ELF查看(readelf)

      • main.c如下

image

  • 1.c如下

image

  • 动态加载流程

    • 动态加载基本流程

image

  • ELF Header内容(readelf -h main/readelf -l main)

image

image

  • 动态链接技术的出现,解决了静态库耦合依赖,以及浪费内存问题。使得多个进程共享一个动态库成为可能。 在一定程度上节省了内存。延迟绑定(Lazy building)以及地址无关技术(PIC position-Independent-Code )的运用,让这个优势更加明显。

3. 技术简介

  • 动态链接技术说白了就是解决了一个重定位问题。把程序原本链接时候符号重定位变成了运行时候动态重定位。 是如何做的呢?

  • 其实就是延迟绑定。存放函数地址的数据表,称为全局偏移表(GOT, Global Offset Table),而那个额外代码段表, 称为程序链接表(延迟绑定技术)(PLT,Procedure Link Table)。

  • 一切的一切,都是这两个表的杰作: PLTGOT

  • 先用官方枯燥无味的话大概总结下这个过程

TIP:

当需要动态链接时,内核会引导动态链接(ELF 解释器),该链接首先会初始化自身,然后加载指定的共享对象(已加载则不必)。接着它会执行必要的再定位,包括目标共享对象所使用的共享对象。LD_LIBRARY_PATH 环境变量定义查找可用共享对象的位置。定义完成后,控制权会被传回到初始程序以开始执行。
重定位是通过一个称为 GOT和PLT的间接机制来处理的。这些表格提供了 ld-linux.so 在再定位过程中加载的外部函数和数据的地址。这意味着无需改动需要间接机制(即,使用这些表格)的代码:只需要调整这些表格。一旦进行加载,或者只要需要给定的函数,就可以发生再定位(稍候在 用 Linux 进行动态加载 小节中会看到更多的差别)。
再定位完成后,动态链接器就会允许任何加载的共享程序来执行可选的初始化代码。该函数允许库来初始化内部数据并备之待用。这个代码是在上述 ELF 映像的 .init 部分中
定义的。在卸载库时,它还可以调用一个终止函数(定义为映像的 .fini 部分)。当初始化函数被调用时,动态链接器会把控制权转让给加载的原始映像。
  • 下边会用更加人性化的方法分析下这个过程

4. PLT和GOT详解

4.1. 重定位

  • 符号重定位,本质上是要解决当前编译单元如何访问“外部”符号这个问题。

    • ELF头部信息:

image

  • 对于可执行文件来说,符号重定位是在链接阶段完成的。但是动态库是在运行时候才加载进来 所以,对于动态库里符号的重定位,只能推迟。称作动态加载(Dynamic Loading,DL)

  • redaelf 命令,可以查看ELF(Executable and Linking Format,ELF)文件

    • readelf -r 查看重定位段情况

    • readelf -d 可以看到程序所需动态库

  • 链接时符号重定位举例

    • 代码1.c 和 main.c

image

image

action direction [log] [quick] on interface [af] [proto protocol] \
from src_addr [port src_port] to dst_addr [port dst_port] \
[tcp_flags] [state]
以上是自己的一点总结